package com.xiyu.service.service.bed;

import com.xiyu.service.model.bed.building.BuildingInfo;
import com.xiyu.service.model.bed.building.BuildingInfoDraft;
import com.xiyu.service.repository.bed.building.BuildingInfoRepository;
import com.xiyu.service.vo.bed.bedInfo.BedInfoPageQueryOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoPageQueryInput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoGetByRoomIdOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoListQueryRoomTypeOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoListQueryBedTypeOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoSingleGetRoomTypeOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoSingleGetBedTypeOutput;
import com.xiyu.service.model.bed.bed.BedInfo;
import com.xiyu.service.model.bed.bed.BedType;
import com.xiyu.service.model.bed.room.RoomInfo;
import com.xiyu.service.model.bed.room.RoomInfoDraft;
import com.xiyu.service.model.bed.room.RoomInfoProps;
import com.xiyu.service.model.bed.room.RoomType;
import com.xiyu.service.repository.bed.bed.BedInfoRepository;
import com.xiyu.service.repository.bed.bed.BedTypeRepository;
import com.xiyu.service.repository.bed.room.RoomInfoRepository;
import com.xiyu.service.repository.bed.room.RoomTypeRepository;
import com.xiyu.service.vo.bed.bedInfo.BedInfoRoomUpdateInput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoRoomCreateInput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoBedTypeUpdateInput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoBedTypeCreateInput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoBedTypeQueryOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoBedTypeQueryInput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoRoomTypeUpdateInput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoRoomTypeCreateInput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoRoomTypeQueryOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoRoomTypeQueryInput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoGetBedOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoGetRoomOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoRoomQueryOutput;
import com.xiyu.service.vo.bed.bedInfo.BedInfoRoomQueryInput;
import org.babyfish.jimmer.DraftObjects;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.babyfish.jimmer.Page;
import java.util.*;

import org.springframework.transaction.annotation.Transactional;
import com.xiyu.service.framework.web.web.core.pojo.PageResult;
import com.xiyu.service.convert.bed.BedInfoConvert;

import static com.xiyu.service.framework.exception.util.ServiceExceptionUtil.exception;
import static com.xiyu.service.errorCode.bed.BedInfoErrorCode.*;

/**
 * 床位信息 Service 实现类
 */
@Service
@Validated
public class BedInfoServiceImpl implements BedInfoService {

    @Resource
    private RoomInfoRepository roomInfoRepository;

    @Resource
    private BedInfoRepository bedInfoRepository;

    @Resource
    private RoomTypeRepository roomTypeRepository;

    @Resource
    private BedTypeRepository bedTypeRepository;

    @Resource
    private BuildingInfoRepository buildingInfoRepository;

    @Override
    public PageResult<BedInfoRoomQueryOutput> roomQuery(BedInfoRoomQueryInput inputVO) {
        Page<RoomInfo> roomInfoPage = roomInfoRepository.pageQuery(inputVO);
        List<BedInfoRoomQueryOutput> listInfoPage = BedInfoConvert.INSTANCE.roomQueryPageOutputConvert(roomInfoPage.getRows());
        return new PageResult<>(listInfoPage, roomInfoPage.getTotalRowCount());
    }

    @Override
    public BedInfoGetRoomOutput getRoom(Long id) {
        Optional<RoomInfo> optionalRoomInfo = roomInfoRepository.findDetailById(id);
        if(!optionalRoomInfo.isPresent())
            throw exception(ROOM_NOT_EXIST);
        return BedInfoConvert.INSTANCE.getRoomOutputConvert(optionalRoomInfo.get());
    }

    @Override
    public BedInfoGetBedOutput getBed(Long id) {
        Optional<BedInfo> optionalBedInfo = bedInfoRepository.findById(id);
        if(!optionalBedInfo.isPresent())
            throw exception(BED_NOT_EXIST);
        return BedInfoConvert.INSTANCE.getBedOutputConvert(optionalBedInfo.get());
    }

    @Override
    public PageResult<BedInfoRoomTypeQueryOutput> roomTypeQuery(BedInfoRoomTypeQueryInput inputVO) {
        Page<RoomType> roomTypePage = roomTypeRepository.pageQuery(inputVO);
        List<BedInfoRoomTypeQueryOutput> listRoomType = BedInfoConvert.INSTANCE.roomTypeQueryPageOutputConvert(roomTypePage.getRows());
        return new PageResult<>(listRoomType, roomTypePage.getTotalRowCount());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long roomTypeCreate(BedInfoRoomTypeCreateInput inputVO) {
        RoomType newRoomType = BedInfoConvert.INSTANCE.roomTypeCreateInputConvert(inputVO);
        Optional<RoomType> dupRoomType = roomTypeRepository.findFirstByTypeName(inputVO.getTypeName());
        if(dupRoomType.isPresent())
            throw exception(ROOM_TYPE_EXIST);
        newRoomType = roomTypeRepository.insert(newRoomType);
        return newRoomType.id();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean roomTypeUpdate(BedInfoRoomTypeUpdateInput inputVO) {
        RoomType updateRoomType = BedInfoConvert.INSTANCE.roomTypeUpdateInputConvert(inputVO);
        Optional<RoomType> dupRoomType = roomTypeRepository.findFirstByTypeNameAndIdNot(updateRoomType.typeName(), updateRoomType.id());
        if(dupRoomType.isPresent())
            throw exception(ROOM_TYPE_EXIST);
        roomTypeRepository.update(updateRoomType);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean roomTypeDeleted(Long id) {
        Optional<RoomType> deleteRoomType = roomTypeRepository.findById(id);
        if(!deleteRoomType.isPresent())
            throw exception(ROOM_TYPE_NOT_EXIST);
        roomTypeRepository.deleteById(id);
        return true;
    }

    @Override
    public PageResult<BedInfoBedTypeQueryOutput> bedTypeQuery(BedInfoBedTypeQueryInput inputVO) {
        Page<BedType> bedTypePage = bedTypeRepository.pageQuery(inputVO);
        List<BedInfoBedTypeQueryOutput> bedTypeList = BedInfoConvert.INSTANCE.bedTypeQueryPageOutputConvert(bedTypePage.getRows());
        return new PageResult<>(bedTypeList, bedTypePage.getTotalRowCount());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long bedTypeCreate(BedInfoBedTypeCreateInput inputVO) {
        Optional<BedType> opDupBedType = bedTypeRepository.findFirstByTypeName(inputVO.getTypeName());
        if(opDupBedType.isPresent()){
            throw exception(ROOM_TYPE_EXIST);
        }
        BedType newBedType = BedInfoConvert.INSTANCE.bedTypeCreateInputConvert(inputVO);
        newBedType = bedTypeRepository.insert(newBedType);
        return newBedType.id();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean bedTypeUpdate(BedInfoBedTypeUpdateInput inputVO) {
        Optional<BedType> opDupBedType = bedTypeRepository.findFirstByTypeNameAndIdNot(inputVO.getTypeName(), inputVO.getId());
        if(opDupBedType.isPresent()){
            throw exception(ROOM_TYPE_EXIST);
        }
        BedType updateBedType = BedInfoConvert.INSTANCE.bedTypeUpdateInputConvert(inputVO);
        bedTypeRepository.update(updateBedType);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean bedTypeDeleted(Long id) {
        Optional<BedType> opBedType = bedTypeRepository.findById(id);
        if(!opBedType.isPresent()){
            throw exception(ROOM_TYPE_NOT_EXIST);
        }
        bedTypeRepository.deleteById(id);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long roomCreate(BedInfoRoomCreateInput inputVO) {
        Optional<RoomInfo> opDupRoom = roomInfoRepository.findFirstByRoomName(inputVO.getRoomName());
        if(opDupRoom.isPresent()){
            throw exception(ROOM_EXIST);
        }
        RoomInfo newRoom = BedInfoConvert.INSTANCE.roomCreateInputConvert(inputVO);
        newRoom = RoomInfoDraft.$.produce(newRoom, draft -> {
            draft.setBedNumber(inputVO.getBeds().size());
        });
        newRoom = roomInfoRepository.insert(newRoom);

        Optional<BuildingInfo> optionalBuildingInfo = buildingInfoRepository.findById(Objects.requireNonNull(newRoom.buildingId()));
        if(!optionalBuildingInfo.isPresent()){
            throw exception(ROOM_BUILDING_NOT_EXIST);
        }
        BuildingInfo updateBuildingInfo = BuildingInfoDraft.$.produce(optionalBuildingInfo.get(), draft -> {
            draft.setBedNumber(draft.bedNumber() + inputVO.getBeds().size()).setRoomNumber(draft.roomNumber() + 1);
        });
        buildingInfoRepository.update(updateBuildingInfo);
        return newRoom.id();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean roomUpdate(BedInfoRoomUpdateInput inputVO) {
        Optional<RoomInfo> opDupRoom = roomInfoRepository.findFirstByRoomNameAndIdNot(inputVO.getRoomName(), inputVO.getId());
        if(opDupRoom.isPresent()){
            throw exception(ROOM_EXIST);
        }

        Optional<RoomInfo> optionalOldRoomInfo = roomInfoRepository.findById(inputVO.getId());
        if(!optionalOldRoomInfo.isPresent()){
            throw exception(ROOM_NOT_EXIST);
        }

        RoomInfo updateRoom = BedInfoConvert.INSTANCE.roomUpdateInputConvert(inputVO);
        int bedNumber = 1005001000;
        for(BedInfoRoomUpdateInput.bed bed : inputVO.getBeds()){
            if(Objects.equals(bed.getOperateType(), "delete")){
                bedInfoRepository.deleteById(bed.getId());
            }else if(Objects.equals(bed.getOperateType(), "new")){
                Optional<BedInfo> opDupBed = bedInfoRepository.findFirstByBedName(bed.getBedName());
                if(opDupBed.isPresent()){
                    throw exception(BED_EXIST);
                }
                BedInfo newBed = BedInfoConvert.INSTANCE.roomUpdateBedInputConvert(bed);
                bedInfoRepository.insert(newBed);
                bedNumber++;
            }else{
                Optional<BedInfo> opDupBed = bedInfoRepository.findFirstByBedNameAndIdNot(bed.getBedName(), bed.getId());
                if(opDupBed.isPresent()){
                    throw exception(BED_EXIST);
                }
                BedInfo updateBed = BedInfoConvert.INSTANCE.roomUpdateBedInputConvert(bed);
                bedInfoRepository.update(updateBed);
                bedNumber++;
            }
        }
        Integer finalBedNumber = bedNumber;
        updateRoom = RoomInfoDraft.$.produce(updateRoom, draft -> {
            DraftObjects.unload(draft, RoomInfoProps.BEDS);
            draft.setBedNumber(finalBedNumber);
        });
        roomInfoRepository.update(updateRoom);

        Optional<BuildingInfo> optionalBuildingInfo = buildingInfoRepository.findById(Objects.requireNonNull(updateRoom.buildingId()));
        if(!optionalBuildingInfo.isPresent()){
            throw exception(ROOM_BUILDING_NOT_EXIST);
        }

        RoomInfo finalUpdateRoom = updateRoom;
        BuildingInfo updateBuildingInfo = BuildingInfoDraft.$.produce(optionalBuildingInfo.get(), draft -> {
            draft.setBedNumber(draft.bedNumber() - optionalOldRoomInfo.get().bedNumber() + finalUpdateRoom.bedNumber());
        });
        buildingInfoRepository.update(updateBuildingInfo);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean roomDeleted(Long id) {
        Optional<RoomInfo> optionalRoomInfo = roomInfoRepository.findById(id);
        if(!optionalRoomInfo.isPresent()){
            throw exception(ROOM_NOT_EXIST);
        }
        roomInfoRepository.deleteById(id);
        Optional<BuildingInfo> optionalBuildingInfo = buildingInfoRepository.findById(Objects.requireNonNull(optionalRoomInfo.get().buildingId()));
        if(!optionalBuildingInfo.isPresent()){
            throw exception(ROOM_BUILDING_NOT_EXIST);
        }
        BuildingInfo updateBuildingInfo = BuildingInfoDraft.$.produce(optionalBuildingInfo.get(), draft -> {
            draft.setRoomNumber(draft.roomNumber() - 1);
        });
        buildingInfoRepository.update(updateBuildingInfo);
        return true;
    }


    @Override
    public BedInfoSingleGetBedTypeOutput singleGetBedType(Long id) {
        Optional<BedType> optionBedType = bedTypeRepository.findById(id);
        if(!optionBedType.isPresent()){
            throw exception(BED_TYPE_NOT_EXIST);
        }
        return BedInfoConvert.INSTANCE.singleGetBedTypeOutputConvert(optionBedType.get());

    }

    @Override
    public BedInfoSingleGetRoomTypeOutput singleGetRoomType(Long id) {
        Optional<RoomType> optionRoomType = roomTypeRepository.findById(id);
        if(!optionRoomType.isPresent()){
            throw exception(ROOM_TYPE_NOT_EXIST);
        }
        return BedInfoConvert.INSTANCE.singleGetRoomTypeOutputConvert(optionRoomType.get());

    }

    @Override
    public List<BedInfoListQueryBedTypeOutput> listQueryBedType() {
        List<BedType> listBedType = bedTypeRepository.findAll();
        return BedInfoConvert.INSTANCE.listQueryBedTypeOutputConvert(listBedType);

    }

    @Override
    public List<BedInfoListQueryRoomTypeOutput> listQueryRoomType(){
        List<RoomType> listRoomType = roomTypeRepository.findAll();
        return BedInfoConvert.INSTANCE.listQueryRoomTypeOutputConvert(listRoomType);
    };

    @Override
    public List<BedInfoGetByRoomIdOutput> getByRoomId(Long id) {
        List<BedInfo> bedInfoList = bedInfoRepository.findByRoomId(id);
        return BedInfoConvert.INSTANCE.getByRoomIdOutputConvert(bedInfoList);
    }

    @Override
    public PageResult<BedInfoPageQueryOutput> pageQuery(BedInfoPageQueryInput inputVO) {
        Page<BedInfo> pageBedInfo = bedInfoRepository.pageQuery(inputVO);
        List<BedInfoPageQueryOutput> listBedInfo = BedInfoConvert.INSTANCE.pageQueryOutputConvert(pageBedInfo.getRows());
        return new PageResult<>(listBedInfo, pageBedInfo.getTotalRowCount());
    }

}
